﻿/* ============================================================================
(c) Copyright 2012 Hewlett-Packard Development Company, L.P.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge,publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
============================================================================ */
using System.Net;
using System.Net.Http;
using System.Collections.Specialized;
using HPCloud.Objects.Utility;
using System;
using System.IO;
using System.Net.Http.Headers;
using System.Web;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net.Http.Formatting;
using System.Collections.Generic;
using System.Runtime.Serialization;
using HPCloud.Objects.Domain.Compute.Servers;
using System.Runtime.Remoting.Channels.Http;
using System.Text;

namespace HPCloud.Objects.DataAccess
{
       public enum ResponseFormat
       {
           data = 0,
           xml = 1,
           json = 2,
           none = 3
       }

       public enum MediaType
       {
           xml   = 0,
           json  = 1
       }

    public class BaseRepository : HttpClient
    {
        private string _lastResponseContent;
        private HttpResponseMessage _lastResponseMessage;     
        private RepositoryContext _context;
        private HttpClient _client = null;
        private MediaType _mediaType = MediaType.xml;

 
        #region Ctors
//==================================================================================================================
/// <summary>
/// 
/// </summary>
//==================================================================================================================
        public BaseRepository()
        { }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
//==================================================================================================================
        public BaseRepository(RepositoryContext context)
        {
            this._context = context;          
        }
        #endregion
        #region Properties
//==================================================================================================================
/// <summary>
/// 
/// </summary>
//==================================================================================================================
        protected MediaType MediaType
        {
            get { return _mediaType; }
            set { _mediaType = value; }
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
//==================================================================================================================
        protected RepositoryContext Context
        {
            get { return _context; }
            set { _context = value; }
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
//==================================================================================================================
        public HttpResponseMessage LastResponseMessage
        {
            get { return _lastResponseMessage; }
        }
        #endregion
        #region Methods
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="status"></param>
/// <returns></returns>
//==================================================================================================================
        protected bool IsStatusNormal(HttpStatusCode status)
        {
            if (status != HttpStatusCode.OK &&
                status != HttpStatusCode.Created &&
                status != HttpStatusCode.NoContent &&
                status != HttpStatusCode.Accepted
                )
            {
                return false;
            }
            else
            {
                return true;
            }
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <returns></returns>
//==================================================================================================================
        protected MediaTypeFormatter CreateFormatter()
        {
            if (this.MediaType == DataAccess.MediaType.xml)
            {                
                XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter();
                formatter.UseXmlSerializer      = true;
                
                return formatter;
            }
            else if (this.MediaType == DataAccess.MediaType.json)
            {
                return new JsonMediaTypeFormatter();
            }
            else
            {
                return null;
            }
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
//==================================================================================================================
        protected List<MediaTypeFormatter> CreateMediaTypeFormatters()
        {
            List<MediaTypeFormatter> formatters = new List<MediaTypeFormatter>();

            if (this.MediaType == DataAccess.MediaType.xml)
            {
                XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter();
                formatter.UseXmlSerializer      = true;
                formatters.Add(formatter);
                return formatters;
            }
            else if (this.MediaType == DataAccess.MediaType.json)
            {                 
                 formatters.Add(this.CreateFormatter());
                 JsonMediaTypeFormatter jsonMTF = new JsonMediaTypeFormatter();
                 formatters.Add(jsonMTF);
                 return formatters;
            }           
            else
            {
                return null;
            }
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <returns></returns>
//==================================================================================================================
        protected HttpClient CreateHttpClient(NameValueCollection headers)
        {
            HttpClient client = this.CreateHttpClient();
         
            if (headers != null)
            {
                foreach (string key in headers.AllKeys)
                {
                    client.DefaultRequestHeaders.Add(key, headers[key]);                   
                }
            }
            return client;
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <returns></returns>
//==================================================================================================================
        protected HttpClient CreateHttpClient()
        {
            int timeout                         = this._context.Settings.HttpTimeoutInterval;
            HttpClientHandler handler           = new HttpClientHandler();
            handler.MaxRequestContentBufferSize = 19800000;
            
            
            if (_client == null)
                _client = new HttpClient(handler);
            else
                return _client;

            _client.MaxResponseContentBufferSize = 1053387136;
            _client.Timeout = new TimeSpan(0, 0, 20, 1, 1);
            _client.DefaultRequestHeaders.Add("X-Auth-Token", this.Context.AccessToken.Id);
            _client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(this.Context.ProductName, this.Context.Version));   
            

            if (this.MediaType == DataAccess.MediaType.xml)
               _client.DefaultRequestHeaders.Add("Accept", "application/xml");  
            else if (this.MediaType == DataAccess.MediaType.json)
                _client.DefaultRequestHeaders.Add("Accept", "application/json");  
                          
            ServicePointManager.ServerCertificateValidationCallback =
                (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
                {
                    return true;
                };

            return _client;        
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="method"></param>
/// <param name="uri"></param>
/// <param name="sourceCollection"></param>
/// <returns></returns>
//==================================================================================================================
        protected HttpRequestMessage CreateRequestMessage(HttpMethod method, Uri uri, NameValueCollection sourceCollection, HttpContent content)
        {
            HttpRequestMessage message = this.CreateRequestMessage(method, uri, sourceCollection);         
            message.Content            = content;
            return message;
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="targetCollection"></param>
/// <param name="?"></param>
/// <returns></returns>
//==================================================================================================================
        protected HttpRequestMessage CreateRequestMessage(HttpMethod method, Uri uri, NameValueCollection sourceCollection)
        {
            HttpRequestMessage message = new HttpRequestMessage(method, uri);

            if (sourceCollection != null)
            {
                foreach (string key in sourceCollection.AllKeys)
                {
                    message.Headers.Add(key, sourceCollection[key]);
                }
            }
            return message;
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="nvc"></param>
/// <returns></returns>
//==================================================================================================================
        private string ToQueryString(NameValueCollection nvc)
        {
           // Contract.Assert(nvc != null, "You must supply a non-null nvc value.");

            if (nvc != null)
                return "?" + string.Join("&", Array.ConvertAll(nvc.AllKeys, key => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(nvc[key]))));
            else
                return null;
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="method"></param>
/// <param name="resourcePath"></param>
/// <param name="headers"></param>
/// <param name="content"></param>
//==================================================================================================================
        protected void InvokeEndpoint(HttpMethod method, string absoluteURI, HttpContent content)
        {
            Uri uri                      = new Uri(absoluteURI);
            HttpClient client            = this.CreateHttpClient();
            HttpResponseMessage response = client.SendAsync(this.CreateRequestMessage(method, uri, null, content)).Result;

            response.Content.LoadIntoBufferAsync();

            if (!this.IsStatusNormal(response.StatusCode))
            {
                throw ExceptionFactory.CreateException(response);
            }
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="method"></param>
/// <param name="resourcePath"></param>
/// <param name="headers"></param>
/// <param name="content"></param>
//==================================================================================================================
        protected T InvokeEndpoint<T>( string absoluteURI, HttpContent content)
        {
            Uri uri = new Uri(absoluteURI);

            HttpClient client = this.CreateHttpClient();
           
                using (HttpResponseMessage response = client.SendAsync(this.CreateRequestMessage(HttpMethod.Post, uri, null, content)).Result)
                {
                    response.Content.LoadIntoBufferAsync();

                    if (!this.IsStatusNormal(response.StatusCode)) {
                        throw ExceptionFactory.CreateException(response);
                    }
                    else {
                        return response.Content.ReadAsAsync<T>(this.CreateMediaTypeFormatters()).Result;
                    }
                }            
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="uri=Offset"></param>
/// <param name="queryString"></param>
/// <returns></returns>
//==================================================================================================================
        protected T InvokeEndpoint<T>(HttpMethod method, string absoluteURI, NameValueCollection queryString, NameValueCollection headers) where T : class
        {
            Uri uri = new Uri(absoluteURI + this.ToQueryString(queryString));
                        
           HttpClient client = this.CreateHttpClient();

           using (HttpResponseMessage response = client.SendAsync(this.CreateRequestMessage(method, uri, headers)).Result)
                {
                    // This allows multiple reads from the same stream..

                    response.Content.LoadIntoBufferAsync();

                    if (!this.IsStatusNormal(response.StatusCode))
                    {
                        throw ExceptionFactory.CreateException(response);
                    }
                    else
                    {
                        //msg.EnsureStatusIsSuccessful();

                        // Deserialize the content back out in the format expected by the client. (of T)

                        try
                        {
                            return response.Content.ReadAsAsync<T>(this.CreateMediaTypeFormatters()).Result;
                        }
                        catch (FileNotFoundException)
                        {
                            return null;
                        }
                    }
                }
           
        }
        //public void UploadAFile()
        //{

        //    var httpClient = new HttpClient();
        //    httpClient.BaseAddress = new Uri(_HostUrl);
        //    httpClient.DefaultRequestHeaders.TransferEncodingChunked = true;

        //    var fileContent = new StreamContent((typeof(SimpleApi).Assembly.GetManifestResourceStream(typeof(SimpleApi), "bigfile.pdf"));
        //    fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        //    var responseMessage = httpClient.PostAsync("sendbigfile", fileContent).Result;

           
        //}

        protected void InvokeEndpoint2(HttpMethod method, string absoluteURI, NameValueCollection headers, Stream content, NameValueCollection queryString)
        {
            //HttpWebRequest request = (HttpWebRequest)WebRequest.Create(absoluteURI);
            //request.AllowWriteStreamBuffering = false;

            //// Set the 'Method' property of the 'Webrequest' to 'POST'.
            //request.Method = "POST";
            //Console.WriteLine("\nPlease enter the data to be posted to the (http://www.contoso.com/codesnippets/next.asp) Uri :");

            //// Create a new string object to POST data to the Url. 
            //string inputData = Console.ReadLine();


            //string postData = "firstone=" + inputData;
            //ASCIIEncoding encoding = new ASCIIEncoding();
            //byte[] byte1 = encoding.GetBytes(postData);

            //// Set the content type of the data being posted.
            //request.ContentType = "application/x-www-form-urlencoded";

            //// Set the content length of the string being posted.
            //request.ContentLength = byte1.Length;

            //Stream newStream = request.GetRequestStream();
            
            //newStream.Write(byte1, 0, byte1.Length);
            //Console.WriteLine("The value of 'ContentLength' property after sending the data is {0}", request.ContentLength);

            //// Close the Stream object.
            //newStream.Close();


       
            //HttpWebRequest  request = (HttpWebRequest) WebRequest.Create(absoluteURI);
            //request.AllowWriteStreamBuffering = false;
            //request.SendChunked = true;
            //request.

 

            #region more bs

            //try
            //{
            //    StreamContent contentStream = new StreamContent(content);
            //    var httpClient = new HttpClient();
            //    httpClient.BaseAddress = new Uri(absoluteURI);
            //    httpClient.DefaultRequestHeaders.
            //    httpClient.DefaultRequestHeaders.TransferEncodingChunked = true;

            //    MultipartFormDataContent content2 = new MultipartFormDataContent();
            //    content2.Add(contentStream);
            //    //var responseMessage = httpClient.PostAsync(absoluteURI, content2).Result;
            //    var responseMessage = httpClient.PostAsync(absoluteURI, content2).Result;
            //}
            //catch (Exception ex)
            //{
            //    int y = 9;
            //}
            #endregion
            #region Another httpclient approach
            //try
            //{
            //    var httpClient = new HttpClient();
            //    httpClient.BaseAddress = new Uri(absoluteURI);
            //    httpClient.DefaultRequestHeaders.TransferEncodingChunked = true;
                
            //    MultipartFormDataContent content2 = new MultipartFormDataContent();
            //    content2.Add(content);
            //    var responseMessage = httpClient.PostAsync(absoluteURI, content2).Result;
            //}
            //catch (Exception ex)
            //{
            //    int y = 9;
            //}
            #endregion
            #region old stuff

            //       StreamContent scontent = new StreamContent(content.ReadAsStreamAsync().Result);
     //       scontent.Headers.ContentType =  new MediaTypeHeaderValue("application/octet-stream");
     //       Uri uri = new Uri(absoluteURI + this.ToQueryString(queryString));

     //       HttpClient client = this.CreateHttpClient(headers);
           
     //           client.DefaultRequestHeaders.TransferEncodingChunked = true;
     //      //   client.MaxResponseContentBufferSize = (int)content.ContentReadStream.Length;

     ////           HttpClientChannel channel =
     ////new HttpClientChannel() { MaxRequestContentBufferSize = 1024 * 1024 * 10 };
     ////           client.Channel = channel;  

     //           //var response = client.PutAsync(uri, scontent).Result;
     //           try
     //           {
     //               var response = client.PutAsync(uri, scontent).Result;
     //               //client.Put(uri, scontent);
     //           }
     //           catch (Exception ex)
     //           {
     //               int h = 8;
     //           }

     //           //if (!this.IsStatusNormal(response.StatusCode))
     //           //{
     //           //    // throw ExceptionFactory.CreateException(msg);
     //           //}
     //           //else
     //           //{
     //           //    //msg.EnsureStatusIsSuccessful();
     //           //}
            //           //client.Dispose();
            #endregion
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="method"></param>
/// <param name="resourcePath"></param>
/// <param name="headers"></param>
/// <param name="content"></param>
//==================================================================================================================
        protected void InvokeEndpoint(HttpMethod method, string absoluteURI, NameValueCollection headers, HttpContent content, NameValueCollection queryString)
        {
            Uri uri                      = new Uri(absoluteURI + this.ToQueryString(queryString));
            HttpClient client            = this.CreateHttpClient(headers);            
            HttpRequestMessage request   = new HttpRequestMessage(method, uri);
            request.Content              = content;
            HttpResponseMessage response = client.SendAsync(request).Result;

            if (!this.IsStatusNormal(response.StatusCode))
            {
                // throw ExceptionFactory.CreateException(msg);
            }
            else
            {
                //msg.EnsureStatusIsSuccessful();
            }
              
            request.Dispose();
            
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="uri=Offset"></param>
/// <param name="queryString"></param>
/// <returns></returns>
//==================================================================================================================
        protected string GetStringResponse(HttpMethod method, string absoluteURI, NameValueCollection queryString, NameValueCollection headers)
        {
            Uri uri = new Uri(absoluteURI + this.ToQueryString(queryString));

            HttpClient client = this.CreateHttpClient();
            
                HttpRequestMessage request = this.CreateRequestMessage(method, uri, headers);

                using (HttpResponseMessage response = client.SendAsync(request).Result)
                {  
                    // Anything other than an Http status of OK or CREATED is considered an exception..                   

                    if (!this.IsStatusNormal(response.StatusCode))
                    {
                        //throw ExceptionFactory.CreateException(msg);
                    }
                    else
                    {
                        // msg.EnsureStatusIsSuccessful();

                        // Deserialize the content back out in the format expected by the client. (of T)

                        return response.Content.ReadAsStringAsync().Result;
                    }
                }
           
            return null;
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="resourcePath"></param>
/// <param name="queryString"></param>
/// <returns></returns>
//==================================================================================================================
        protected void InvokeEndpoint(HttpMethod method, string absoluteURI, NameValueCollection queryString, NameValueCollection headers)
        {
            Uri uri                    = new Uri(absoluteURI + this.ToQueryString(queryString));
            HttpRequestMessage request = this.CreateRequestMessage(method, uri, headers);

            HttpClient client = this.CreateHttpClient();

            HttpResponseMessage response = client.SendAsync(request).Result;

                if (!this.IsStatusNormal(response.StatusCode))
                {
                    throw ExceptionFactory.CreateException(response);
                }
                else
                {
                   // msg.EnsureStatusIsSuccessful();
                }

               
                response.Dispose();
            
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="method"></param>
/// <param name="absoluteURI"></param>
/// <param name="queryString"></param>
/// <param name="headers"></param>
/// <returns></returns>
//==================================================================================================================
        public static T ListObjects<T>(HttpMethod method, string absoluteURI, NameValueCollection queryString, NameValueCollection headers, string authToken) where T : class
        {
            Uri uri                    = new Uri(absoluteURI + "?" + string.Join("&", Array.ConvertAll(queryString.AllKeys, key => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(queryString[key])))));
            HttpRequestMessage request = new HttpRequestMessage(method, uri);
            request.Method             = method;
            HttpClient client          = new HttpClient();

            client.DefaultRequestHeaders.Add("Accept", "application/xml");
            client.DefaultRequestHeaders.Add("X-Auth-Token", authToken);

    
            
                if (queryString != null)
                {
                    //  uri = queryString.MakeQueryString(uri);
                }

                if (headers != null)
                {
                    // client.DefaultHeaders = headers;
                }

                using (HttpResponseMessage msg = client.SendAsync(request).Result)
                {
                    List<MediaTypeFormatter> formatters = new List<MediaTypeFormatter>();
                    XmlMediaTypeFormatter formatter     = new XmlMediaTypeFormatter();
                    formatter.UseXmlSerializer          = true;
                    formatters.Add(formatter);

                    if (msg.StatusCode == HttpStatusCode.MethodNotAllowed)
                    {
                        using (HttpResponseMessage newMsg = client.SendAsync(request).Result)
                        {
                            // This allows multiple reads from the same stream..

                            newMsg.Content.LoadIntoBufferAsync();
                           
                            formatters.Add(formatter);

                            return newMsg.Content.ReadAsAsync<T>(formatters).Result;
                        }
                    }

                    // This allows multiple reads from the same stream..

                    msg.Content.LoadIntoBufferAsync();

                    // Anything other than an Http status of OK or CREATED is considered an exception..

                    //  msg.EnsureStatusIsSuccessful();

                    // Deserialize the content back out in the format expected by the client. (of T)

                    return msg.Content.ReadAsAsync<T>(formatters).Result;
                }
            
        }
//==================================================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="resourcePath"></param>
/// <param name="queryString"></param>
/// <returns></returns>
//==================================================================================================================
        protected T InvokeEndpoint<T>(HttpMethod method,  string absoluteURI, NameValueCollection headers) where T : class
        {
            Uri uri                    = new Uri(absoluteURI);
            HttpRequestMessage request = this.CreateRequestMessage(method, uri, headers);

            HttpClient client = this.CreateHttpClient();

            HttpResponseMessage response = client.SendAsync(request).Result;
                
                // This allows multiple reads from the same stream..

                response.Content.LoadIntoBufferAsync();


                if (response.StatusCode == HttpStatusCode.MultipleChoices)
                {
                    return response.Content.ReadAsAsync<T>(this.CreateMediaTypeFormatters()).Result;
                }
                else if (!this.IsStatusNormal(response.StatusCode))
                {
                    //throw ExceptionFactory.CreateException(msg);
                }
                else
                {
                    return response.Content.ReadAsAsync<T>(this.CreateMediaTypeFormatters()).Result;
                }
                return null;
           
        }
        #endregion
    }
}


